Quotes Provider
Provider supplies quotes of the selected trading instrument, to display this information in the trading widget.
/*** Interface for receiving quotes data** When loading dxCharts library chart, quotes data is taken from this interface** When connecting dxCharts library, developer can implement this interface or use the default implementation [com.devexperts.dxcharts.provider.quotes.DxFeedQuotesProvider] and pass it to the library using [DxChartsDataProviders] data class** Use [dataFlow] to get quotes data** Use [changeSymbol] to change quotes' instrument symbol*/interface DxChartsQuotesProvider {/*** Flow of receiving quotes data** Quotes data is represented by [Quote]*/val dataFlow: StateFlow<Quote?>/*** Changes quotes' instrument symbol** @param symbol instrument symbol*/fun changeSymbol(symbol: String)}
With changeSymbol
method, the symbol of the instrument can be changed.
Data is sent by updating the state of the dataFlow
variable. It is represented as com.devexperts.dxcharts.provider.domain.Quote
object.
/*** Quote - data with instruments' statistics from exchange** Stores ask, bid prices and sizes*/data class Quote(val bidPrice: Double,val askPrice: Double,)
Trading Widget with the quotes passed into it:
Here is the default implementation of DxChartsQuotesProvider
:
/*** Implementation of [DxChartsQuotesProvider] that uses the dxFeed API.** The library "com.devexperts.qd:qds" is used to connect to the dxFeed API.** The process of obtaining quotes is performed in a separate thread [ExecutorService] using [endpoint] from method [connect]:* - [endpoint] connects to the dxFeed API at the [endpointAddress] in the [connect] method.* - A subscription [sub] is created to receive quotes.* - An [eventListener] is added to the subscription [sub] to handle the received quotes.* - When a quote is received in [eventListener], it is transformed into [com.devexperts.dxcharts.provider.domain.Quote]* using the extension function [toDataObject] and sent to [dataFlow].* - Errors encountered during operations are reported via [errorFlow], using instances of [QuoteProviderError].** When the symbol changes [changeSymbol], the subscription [sub] is disconnected from the old symbol and connected to the new one.** When disconnecting [disconnect] from the dxFeed API, the subscription [sub] is disconnected from the symbol and cleared.** @property [endpoint] Object for working with the dxFeed API.* @property [executorService] Object for starting a thread to retrieve quotes.* @property [sub] Subscription object of [endpoint] with the instrument symbol [currentSymbol].* @property [currentSymbol] Current symbol of the instrument.* @property [_dataFlow] Internal [MutableStateFlow] for sending quotes.* @property [dataFlow] [StateFlow] for sending quotes.* @property [_errorFlow] Internal [MutableStateFlow] for sending errors.* @property [errorFlow] [StateFlow] for sending errors.* @property [eventListener] Handler for quotes received from [sub].*/class DxFeedQuotesProvider(private val endpointAddress: String = "") : DxChartsQuotesProvider,DxChartsErrorProvider<QuoteProviderError> {private val endpoint = DXEndpoint.getInstance()private val sub: DXFeedSubscription<Quote> = endpoint.feed.createSubscription(Quote::class.java)private val _dataFlow = MutableStateFlow<com.devexperts.dxcharts.provider.domain.Quote?>(null)override val dataFlow: StateFlow<com.devexperts.dxcharts.provider.domain.Quote?> get() = _dataFlowprivate val _errorFlow = MutableStateFlow<QuoteProviderError?>(null)override val errorFlow: StateFlow<QuoteProviderError?> get() = _errorFlowprivate val eventListener = DXFeedEventListener<Quote> {try {val quote = it.last()_dataFlow.tryEmit(quote?.toDataObject())} catch (e: Exception) {_errorFlow.tryEmit(QuoteProviderError.DataProcessingError("Error during processing quotes: $e.message", e))}}private var currentSymbol: String? = nullprivate var executorService: ExecutorService? = null/*** Connects to the dxFeed API [endpoint] at the [endpointAddress],* subscribes [sub] to receive quotes, connects [eventListener],* and starts the process of obtaining quotes in a separate thread [executorService].* Handles connection errors and updates [_errorFlow].*/fun connect() {try {_errorFlow.tryEmit(null)endpoint.connect(endpointAddress)sub.addEventListener(eventListener)if (executorService == null || executorService?.isShutdown == true) {executorService = Executors.newFixedThreadPool(1)executorService?.execute {try {endpoint.awaitNotConnected()} catch (e: Exception) {_errorFlow.tryEmit(QuoteProviderError.NetworkError("Connection to quote provider was interrupted: $e.message}", e))}}} else {Log.w(TAG, "ExecutorService is already running.")}} catch (e: Exception) {_errorFlow.tryEmit(QuoteProviderError.ConnectionError("Unknown error during connection to quote provider: $e.message", e))}}/*** Removes [eventListener] from [sub], clears [sub] from [endpoint],* disconnects [endpoint] from the dxFeed API, and shuts down [executorService].* Handles disconnection errors and updates [_errorFlow].*/fun disconnect() {try {sub.removeEventListener(eventListener)endpoint.feed.detachSubscriptionAndClear(sub)endpoint.disconnectAndClear()executorService?.shutdown()executorService = null} catch (e: Exception) {_errorFlow.tryEmit(QuoteProviderError.ConnectionError("Unknown error during disconnection of quote provider $e.message", e))}}/*** Extension function for transforming [Quote] into [com.devexperts.dxcharts.provider.domain.Quote].*/private fun Quote.toDataObject() = com.devexperts.dxcharts.provider.domain.Quote(bidPrice = bidPrice,askPrice = askPrice,)/*** Method to change the instrument symbol [currentSymbol] in the subscription [sub].** When the symbol changes [currentSymbol], the subscription [sub] is disconnected from the old symbol,* connected to the new one, and [dataFlow] is cleared.*/override fun changeSymbol(symbol: String) {if (currentSymbol == symbol) {return}currentSymbol?.let {try {sub.removeSymbols(currentSymbol)_dataFlow.tryEmit(null)} catch (e: Exception) {_errorFlow.tryEmit(QuoteProviderError.DataProcessingError("Error during processing quotes: $e.message",e))}}currentSymbol = symboltry {sub.addSymbols(symbol)} catch (e: Exception) {_errorFlow.tryEmit(QuoteProviderError.DataProcessingError("Error adding symbol: $e.message", e))}}companion object {private const val TAG = "DxFeedQuoteProvider"}}/**- Sealed class representing different types of errors that can occur in the provider.*/sealed class QuoteProviderError(override val message: String, override val error: Throwable?) : ProviderError {data class NetworkError(override val message: String,override val error: Throwable? = null) : QuoteProviderError(message, error)data class ConnectionError(override val message: String,override val error: Throwable? = null) : QuoteProviderError(message, error)data class DataProcessingError(override val message: String,override val error: Throwable? = null) : QuoteProviderError(message, error)}